package fuse.LaCollaFS.Metadades;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;

import java.io.IOException;

import java.io.PrintWriter;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.rmi.RemoteException;
import java.util.Date;
import java.util.Hashtable;
import java.util.Vector;

//import org.apache.commons.logging.Log;
//import org.apache.commons.logging.LogFactory;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

import fuse.FuseException;
import fuse.FuseStatfs;
import fuse.LaCollaFS.LaCollaFS;
import fuse.LaCollaFS.Api.*;
import fuse.LaCollaFS.Metadades.Directori;
import fuse.LaCollaFS.Metadades.Estructura;

import fuse.FuseStat;

import LaColla.Api.Api;

import LaColla.core.data.TimestampSummary;
import LaColla.core.data.app.ObjectLaCOLLA;
import LaColla.core.util.Debug;
import LaColla.core.util.constant;

//import com.sun.org.apache.xml.internal.resolver.helpers.Debug;

//import LaColla.core.util.Debug;

public class MetaDades {

	// fuse variables

	private FuseStatfs statfs;

	private Directori tree;

	private String defpath;

	private String defpathmetadades = "/tmp/lacollafsmetadades";

	// parametres de conexió a la colla

	private static final int blockSize = 512;

	//private static final Log log = LogFactory.getLog(LaCollaFS.class);

	private static final int RMIport = 11099;

	private static String appIP;

	private static int apiPort = 19000;

	private static int appPort = 4321;

	private static String username = "admin";

	private static String passwd = "admin";

	private static String groupId = "hola";

	private Api api;

	private String treeObjId;

	private ApiServer aserv;

	private String memberId;

	private String apiid;

	// vectors per guardar els fitxers oberts

	private Hashtable<String, FileInputStream> TableInput = new Hashtable();

	private Hashtable<String, FileOutputStream> TableOutput = new Hashtable();

	/**
	 * Constructor de les metadades
	 * 
	 * 
	 * @param defaultpath
	 *            path per defecte on guardar el docs en local
	 * @throws RemoteException
	 */
	public MetaDades(String path) throws RemoteException {
		tree = new Directori("/", this);
		defpath = path;
		int files = 0;
		int dirs = 0;
		int blocks = 0;
		// conecta a lacolla.....
	//	log.info("extracting  params conections...");
		try {
			appIP = InetAddress.getLocalHost().getCanonicalHostName();
		} catch (UnknownHostException e1) {

			e1.printStackTrace();
		}
		Debug.say("LaCollaFS", appIP);
		ApiServer asas;
		aserv = new ApiServer(appIP, appPort);
		p("Api Server creat..." + aserv);
		api = aserv.resolveApi(apiPort);
		TimestampSummary s;
		s = aserv.readSummary();
		//p("tss:"+s);
		String gapaIP = appIP;
		int gapaPort = 2001;
		apiid = api.login(groupId, username, passwd, gapaIP, gapaPort, appIP,
				appPort, aserv.readSummary());
		// en aquest punt encara pot no haber arribat el
		// newConnectedMemberEvent, s'hauria de bloquejar..
		try {
			Thread.sleep(1000);
		} catch (Exception e) {
		} // esperem rebre l'event
		memberId = ((ApiImpl) aserv.getASAI()).getMemberId();
		p("Membre conectat, el meu id és : " + memberId);

		// inicialitza el statfs
		statfs = new FuseStatfs();
		statfs.blocks = blocks;
		statfs.blockSize = blockSize;
		statfs.blocksFree = 0;
		statfs.files = files + dirs;
		statfs.filesFree = 0;
		statfs.namelen = 2048;

		File file = new File(defpathmetadades);
		file.mkdirs();
		p("dir creat!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");

		this.DesaTree();

	}

	/**
	 * Imprimeix per pantalla
	 * 
	 * @param o
	 */
	private static void p(Object o) {
		System.out.println(o);
	}

	/**
	 * Obte el FuseStat del Objecte path
	 * 
	 * @param path
	 * @return
	 * @throws FuseException
	 */
	public FuseStat ObtenirStat(String path) throws FuseException {

		Estructura node;
		FuseStat stat = null;

		node = ObtenirNode(path);
		if (node == null) {
			throw new FuseException("No existeix el path")
					.initErrno(FuseException.ENOENT);
		} else
			stat = node.getStat();
		return stat;
	}

	/**
	 * Canvia el FuseStat d'un objecte i guarda el arbre de les metadades a
	 * lacolla
	 * 
	 * @param path
	 *            node a canviar l'estat
	 * @param estat
	 *            FuseStat amb els canvis a realitzar
	 */
	public void CanviarStat(String path, FuseStat estat) {

		Estructura node;
		node = ObtenirNode(path);
		node.setStat(estat);
		DesaTree();

	}

	/**
	 * Obté l'Objecte amb el node demanat.
	 * 
	 * @param path
	 *            path del objecte a recuperar
	 * @return retorna el Objecte en questió
	 */
	public Estructura ObtenirNode(String path) {
		Estructura node;

		this.ObtenirTree();
		node = tree.BuscarFill(path);

		return node;
	}

	/**
	 * Elimina node del arbre de directoris TODO
	 * 
	 * @param path
	 *            Path del arbre a eliminar
	 */
	public void EliminarNode(String path) {

	}

	public void ObrirFitxer(String path, boolean lectura, boolean escriptura) {
		Fitxer fit;
	//	int fh = 0;
		fit = (Fitxer) this.ObtenirNode(path);
		File fichero = new File(defpath + path);
		p("fitxer path a crear..."+ fichero.getAbsolutePath()+",length:  "+fichero.length()+", fichero nom: "+fichero.getName());
		p("new date "+ new Date());
		String groupId = "hola";
		ObjectLaCOLLA obj=null;
		 obj=new ObjectLaCOLLA(fit.getIdObjecte(),new Date(),"",groupId,fichero.length(),fichero.getAbsolutePath(),fichero.getName());
		//obj.setObjectId(fit.getIdObjecte());
		try {
		//	api.getObject(obj, fichero.getAbsolutePath());
		} catch (Exception e) {
			p("fitxer no trobat a la colla!!!!");
			e.printStackTrace();
		}
		p("hooolaaa");
		if (lectura) { // lectura!!!
			try {
				FileInputStream fis = new FileInputStream(fichero);
				TableInput.put(path, fis);
				
			//	p("obert per lectura, fh =  " + fh);
			} catch (FileNotFoundException e) {
				p("fitxer no trobat a la cache!!!!!!!!!");
				e.printStackTrace();
			}

		}
		if (escriptura) { // escriptura
			try {
				FileOutputStream fos = new FileOutputStream(fichero);
				TableOutput.put(path,fos);
				//fh = TableOutput.indexOf(fos);
				// TableOutput.put(path, fos);
		//		p("obert per escriptura, fh = " + fh);
			} catch (FileNotFoundException e) {
				p("fitxer no trobat a la cache!!!!!!!!!");
				e.printStackTrace();
			}
		}

	//	return fh;

	}

	public void Llegirfitxer(String path, long offset, ByteBuffer buf) {

		FileInputStream fitxer;
		fitxer = TableInput.get(path);

		try {

			int tamany = buf.capacity();
			byte[] buffer = new byte[tamany];

			int limit = fitxer.read(buffer);
			//p("que retorna el read?? = " + limit);
			for (int i = 0; i < limit; i++) { // començant per
				buf.put(buffer[i]);
			//	p(buffer[i]);
			}

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		p(buf);
		// return buf;
	}

	/**
	 * Escriu canvis en un fitxer i el guarda a lacolla Moficia l'estat del
	 * Fitxer en l'estructura a les metadades.
	 * 
	 * TODO REPASAR OFFSET!!!! ara mateix no fa el que hauria de fer!!!!
	 * 
	 * @param path
	 *            path del fitxer a modifcar
	 * @param buf
	 *            dades a afegir al fitxer
	 * @param offset
	 *            offset on començar a escriure en el fitxer
	 */
	public void Escriurefitxer(String path, ByteBuffer buf, long offset) {
		// buf.position((int) offset);
		FileOutputStream fos;
		fos = TableOutput.get(path);
		try {

			for (long i = 0; i < buf.capacity(); i++) {
				if (i < offset) {
					// no se com moure el punter!!!!
				} else {
					fos.write(buf.get((int) i));
				}
				
				Estructura fit =  this.ObtenirNode(path);
				FuseStat stat = fit.getStat();
				long v = (offset + buf.capacity());
				stat.size = max(v,stat.size);
				fit.setStat(stat);
				p("tamany fitxer: "+ stat.size);

			}
			
			

		} catch (FileNotFoundException e) {
			p("error al no trobar un fitxer");
			// e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private long max(long a	, long b) {
		if (a> b) return a;
		else return b;
	}

	public void TancarFitxer(String path) {
		FileInputStream fis;
		FileOutputStream fos;

		if (TableInput.contains(path)) {
			fis = TableInput.get(path);
			try {
				fis.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			TableInput.remove(path);
		}
		if (TableOutput.contains(path)) {
			fos = TableOutput.get(path);
			try {
				fos.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			TableOutput.remove(path);
		}

	}

	public void PujaFitxer(String path) {

		// pujar fitxer a la colla

		File fichero = new File(defpath + path);
		Fitxer fit;
		fit = (Fitxer) this.ObtenirNode(path);
				
		ObjectLaCOLLA obj = new ObjectLaCOLLA(null, new Date(), "", groupId,
				fichero.length(), fichero.getAbsolutePath(), fichero.getName());
		try {
			obj = api.putObject(obj);
		} catch (RemoteException e) {

			e.printStackTrace();
		}
		fit.setIdObjecte(obj.getObjectId());
		FuseStat estat = fit.getStat();
		estat.size = fichero.length();
		estat.blocks = (int) ((estat.size + 511L) / 512L);
		fit.setStat(estat);
		this.DesaTree();

	}

	

	/**
	 * Obté l'arbre de les metadades del fitxer XML guardat a LaColla
	 * 
	 * @throws FileNotFoundException
	 * @throws JDOMException
	 * @throws IOException
	 */
	public void ObtenirTree() {
		Element root;
		Document doc;
		String xml;
		SAXBuilder builder;

		xml = ((ApiImpl) aserv.getASAI()).getTree();
		// p("xml que tinc... " + xml);
		builder = new SAXBuilder();
		try {

			doc = builder.build(new StringReader(xml));
			root = doc.getRootElement();
			tree.LlegirXML(root);

		} catch (JDOMException e) {
			p("error de JDOM");
			e.printStackTrace();
		} catch (IOException e) {
			p("error del String Reader...");
			e.printStackTrace();
		}

		/*
		 * String groupId = "hola"; ObjectLaCOLLA obj; File file = new
		 * File(defpathmetadades + "/dir.xml"); obj = new ObjectLaCOLLA(null,
		 * new Date(), "", groupId, file.length(), file.getAbsolutePath(),
		 * file.getName());
		 * 
		 * treeObjId = ((ApiImpl) aserv.getASAI()).gettreeObjId(); p("objecte
		 * lacolla a recuperar : " + treeObjId);
		 * 
		 * obj.setObjectId(treeObjId); // p("objecte passat per l'api de
		 * lacolla..."); // p("obtenir objecte de la colla, get object!!!!"+
		 * obj.getObjectId()); try { api.getObject(obj, file.getAbsolutePath()); }
		 * catch (RemoteException e) { p("error al obtenir el xml");
		 * e.printStackTrace(); } // p("objecte aconsegit!!!!!!!!!!!!!!!!!!!"); //
		 * treeObjId = ((ApplicationsSideApiImpl)asas.getASAI()).gettreeObjId();
		 * 
		 * SAXBuilder builder2 = new SAXBuilder(); Document doc2; try { doc2 =
		 * builder2.build(new FileInputStream(defpathmetadades + "/dir.xml"));
		 * Element rootl = doc2.getRootElement(); tree.LlegirXML(rootl); } catch
		 * (FileNotFoundException e) { // TODO Auto-generated catch block
		 * e.printStackTrace(); } catch (JDOMException e) { // TODO
		 * Auto-generated catch block e.printStackTrace(); } catch (IOException
		 * e) { // TODO Auto-generated catch block e.printStackTrace(); }
		 */
	}

	/**
	 * Guarda el l'Arbre de les metadades en un fitxer XML i el desa a laCOlla
	 * 
	 */
	@SuppressWarnings("deprecation")
	public void DesaTree() {
		String arbrexml = null;
		StringWriter sw = new StringWriter();
		// p("-----------------Vaig a desar el arbre de les
		// metadades--------------------");
		Element rootdir = tree.CrearXML();
		XMLOutputter outputter = new XMLOutputter("", true);

		try {
			outputter.output(new Document(rootdir), sw);
			arbrexml = sw.toString();
			p(arbrexml);//SYSOUT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!SYSOUT!!!!!!!!!!!!!

		} catch (FileNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		// p("xml creat:..." + arbrexml);
		// arbrexml = rootdir.toString();
		// arbrexml = (arbrexml.split(" "))[1];
		// arbrexml = (arbrexml.split("]"))[0];
		// p(arbrexml);
		LaColla.core.data.app.Event evt;
		LaColla.core.data.Timestamp s = new LaColla.core.data.Timestamp();
		
		
		evt = new LaColla.core.data.app.Event(memberId, apiid, groupId, "pepito1",
				s, "pepito4", constant.eventApplication, arbrexml, "pepito2");
		try {
			// modifico el xml de les metadades en local abans de llençar el
			// event aixi, ens asegurem de que el sistema de fitxers no dona un
			// error al usuari al comprovar els canvis fets ( per exemple, al
			// fer un mkdir, dp de el mkdir fa un getatr, si el event encara no
			// ha arribat, donara un error de que el directori no s'ha pogut
			// crear quan s'ha creat sense cap problema

			((ApiImpl) aserv.getASAI()).setTree(arbrexml);
			p("enviant event....");
			api.disseminateEvent(groupId, evt);

		} catch (RemoteException e) {

			e.printStackTrace();
		}

		/*
		 * XMLOutputter outputter2 = new XMLOutputter("", true);
		 * 
		 * try { outputter2.output(new Document(rootdir), new FileOutputStream(
		 * defpathmetadades + "/dir.xml")); } catch (FileNotFoundException e1) {
		 * p("Error amb el fitxer!!!! al intentar escriure el xml al disc"); }
		 * catch (IOException e1) { p("error amb io al escriure el xml al
		 * disc!!!"); // e1.printStackTrace(); } // api.getInfoGroup(arg0, arg1,
		 * arg2); obtenir grupId, mentrestant... String groupId = "hola";
		 * ObjectLaCOLLA obj = null; File file = new File(defpathmetadades +
		 * "/dir.xml"); obj = new ObjectLaCOLLA(null, new Date(), "", groupId,
		 * file.length(), file.getAbsolutePath(), file.getName()); p("obtenir el
		 * treeObjId de la colla..."+ aserv);
		 * 
		 * treeObjId = ((ApiImpl) aserv.getASAI()).gettreeObjId(); p("IDObject
		 * abans de fer el put...." + treeObjId); // obj.setObjectId(treeObjId);
		 * 
		 * try { obj = api.putObject(obj); p("IdObject dp de fer el put.... " +
		 * obj.getObjectId()); } catch (RemoteException e) {
		 * 
		 * p("!!!!!!!!!!!!!!error al fer el putobject!!!!!!!!!!!!!!!!!!!11"); //
		 * e.printStackTrace(); } p("--------------dir.xml
		 * ObjecteDipositat---------------------: "); p("id del objecte
		 * dir.xml...." + treeObjId); try { Thread.sleep(1000); } catch
		 * (Exception e) { } treeObjId = ((ApiImpl)
		 * aserv.getASAI()).gettreeObjId();
		 * 
		 * p("IdObject que em torna l'api de la colla..." + treeObjId);
		 */
	}

	/**
	 * Retorna el Stat del FileSystem TODO
	 * 
	 * @return
	 */
	public FuseStatfs getStat() {

		return statfs;
	}

	/**
	 * Obté el nom d'un objecte
	 * 
	 * @param path
	 * @return
	 */
	public String ObtenirNom(String path) {
		String[] pathParts = path.split("/");

		return pathParts[pathParts.length - 1];
	}

	/**
	 * Obté el path del pare d'el objecte
	 * 
	 * @param path
	 * @return
	 */
	public String ObtenirDir(String path) {

		String[] pathParts = path.split("/");
		String pathnode;
		if (pathParts.length <= 2)
			pathnode = "/";
		else
			pathnode = ""; //

		for (int i = 1; i < pathParts.length - 1; i++) {
			pathnode = pathnode + "/" + pathParts[i];
		}

		return pathnode;
	}

	public void close() {
		try {
			api.logout(groupId,memberId,apiid);
			aserv.close();
		} catch (RemoteException e) {
			p("error al suicidar les metadades");
			e.printStackTrace();
		}
	
		
		
	
	}

	public void moureEstructura(String from, String to) {
		String pareto;
		String nomto;

		pareto = ObtenirDir(to); // nom ruta desti
		nomto = ObtenirNom(to); // nom fitxer desti

		Estructura nodefrom;
		Directori nodeto;
		
		p("pareto "+ pareto);
		nodeto = (Directori) ObtenirNode(pareto); 
		nodefrom = ObtenirNode(from); // element a moure
		//nodeto.AfegirFill(nodefrom);
		
		nodefrom.getPare().EliminarFillRecursiu(nodefrom.getNom());
	//	Element xml = ((Directori)nodefrom).CrearXML();
		
		//Directori nou = new Directori(null, null);
	//	nou.LlegirXML(xml);
		p("nodeto buit?"+ nodeto.Buit());
		nodeto.AfegirFill(nodefrom);
		p("nodeto "+ nodeto.getNom());
		
		nodeto.getPare().EliminarFillRecursiu(nodeto.getNom());
		nodeto.getPare().AfegirFill(nodeto);
		
		
		// no se per que el tree de les metadades no s'actualitza, en canvi en el mkdir si... i es gairebe igual...

		DesaTree();
		
	}

	

	 

}
